package org.hertsig.magiccardmarket;

import java.io.IOException;
import java.util.Iterator;
import java.util.NoSuchElementException;

import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;

import org.apache.http.HttpResponse;
import org.hertsig.http.ConnectionHelper;
import org.hertsig.magiccardmarket.xml.XmlMarshaller;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Iterators;

/**
 * This iterator iterates over a paged list of results returned from the API. It's passed a base URL, and will query
 * the next page on a {@link #next()} or {@link #hasNext()} call, when neccessary.
 */
@RequiredArgsConstructor
public abstract class PartialContentIterator<T> implements Iterator<T> {
	private static final int PAGE_SIZE = 100;
	private static final int DONE_INDEX = -1;
	
	@NonNull private final Class<? extends Iterable<T>> valueType;
	private int nextStartIndex = 1;
	private Iterator<T> currentPart = Iterators.emptyIterator();
	
	@Override
	public final boolean hasNext() {
		if (!currentPart.hasNext()) {
			if (nextStartIndex == DONE_INDEX) {
				return false;
			}
			
			currentPart = queryNextPart();
		}
		return currentPart.hasNext();
	}
	
	/**
	 * @return true iff the next call to {@link #next()} or {@link #hasNext()} will query the next URL.
	 */
	public boolean nextCallWillQuery() {
		return !currentPart.hasNext() && nextStartIndex != DONE_INDEX;
	}
	
	@SneakyThrows(IOException.class)
	private Iterator<T> queryNextPart() {
		HttpResponse response = openConnection(nextStartIndex);
		
		int responseCode = response.getStatusLine().getStatusCode();
		if (responseCode == ConnectionHelper.HTTP_OK) {
			nextStartIndex = DONE_INDEX;
		}
		else if (responseCode == ConnectionHelper.HTTP_PARTIAL_CONTENT) {
			nextStartIndex += PAGE_SIZE;
		}
		else if (responseCode == ConnectionHelper.HTTP_NO_CONTENT) {
			nextStartIndex = DONE_INDEX;
			return Iterators.emptyIterator();
		}
		else {
			ConnectionHelper.copy(response.getEntity().getContent(), System.out);
			throw new IllegalStateException("Unexpected response code " + responseCode);
		}
		
		return XmlMarshaller.unmarshal(response.getEntity().getContent(), valueType).iterator();
	}
	
	@VisibleForTesting
	abstract HttpResponse openConnection(int nextStartIndex);
	
	@Override
	public final T next() {
		if (!hasNext()) {
			throw new NoSuchElementException();
		}
		
		return currentPart.next();
	}
	
	@Override
	public final void remove() {
		throw new UnsupportedOperationException();
	}
}
